//Wardell Chandler Bagby 
//CS 325 
namespace Project3 
{ 
	using namespace System; 
	using namespace System::IO; 
	using namespace System::Collections; 
	using namespace System::Collections::Generic; 
	using namespace System::Text; 

	/**Entry Class for Testing **/
	public ref class Entry :  public IComparable
	{ 
	public: 
		String^ word; 
		int spamCount; 
		int hamCount; 
		double percentageInSpam; 
		double percentageInHam; 
		Entry()
		{
			word = "";		
		}; 
		Entry(String^ s) 
		{ 
			word = s; 
		}; 
		Entry(String^ s, int sC, int hC, int sP, int hP) 
		{ 
			word = s; 
			spamCount = sC; 
			hamCount = hC; 
			percentageInSpam = sP; 
			percentageInHam = hP; 
		}; 
		virtual int CompareTo(Object^ obj) 
		{
			Entry^ o = safe_cast<Entry^>(obj);
			if (o) 
			{
				return word->CompareTo(o->getWord());
			}
			else
			{
				throw gcnew ArgumentException
					("Object must be of type 'Entry'");
			}
		};
		virtual String^ ToString() override
		{
			return "Word: "+word+"; Spam Count: "+spamCount+"; Ham Count: "+hamCount+"; Percentage In Spam: "+percentageInSpam+"; Percentage In Ham: "+percentageInHam;
		};
		int getSpamCount()
		{return spamCount;}; 
		int getHamCount(){return hamCount;}; 
		double getSpamPercentage(){return percentageInSpam;}; 
		double getHamPercentage(){return percentageInHam;}; 
		void updateSpamCount() 
		{ 
			spamCount++; 
		}; 
		void updateHamCount() 
		{ 
			hamCount++; 
		}; 
		String^ getWord() 
		{ 
			return word; 
		}; 
	};

	public ref class PreRepository 
	{ 
	public: 
		bool sorted;
		PreRepository() 
		{ 
			sorted = false;
			entries = gcnew List<Entry^>(); 
		} 
		List<Entry^>^ entries; //Struggled back and forth between using an List or a Set of some sort. AList for now.  
		Entry^ currEntry; 
		void addEntry(String^ s, bool isSpam) 
		{ 
			System::Collections::IEnumerator^ e = entries->GetEnumerator(); 
			bool found = false; 
			while(e->MoveNext())
			{ 
				currEntry = safe_cast<Entry^>(e->Current);
				//Checks to see if entry is already in List. If it is, increments it's ham/spam count. Otherwise, adds a new Entry with 1 to it's ham/spam count 
				if((currEntry->getWord()->Equals(s))) 
				{                    
					found = true; 
					if(isSpam) 
						currEntry->updateSpamCount(); 
					else
						currEntry->updateHamCount(); 
				} 
			} 
			if(!found) 
			{ 
				sorted = false;
				if(isSpam) 
					entries->Add(gcnew Entry(s,1,0,0,0)); 
				else
					entries->Add(gcnew Entry(s,0,1,0,0)); 
			} 
		} 
		List<Entry^>^ getEntries() 
		{ 
			if(sorted)
				return entries; 
			entries->Sort();
			sorted = true;
			return entries;
		}; 
		String^ getEntriesAsString() 
		{ 
			if(!sorted)
			{
				entries->Sort();
			}
			StringBuilder^ sB = gcnew StringBuilder(); 
			Entry^ currEntry; 
			for(int i=0; i<entries->Count; i++) 
			{ 
				currEntry = (Entry^)entries[i]; 
				sB->AppendLine(currEntry->getWord()+":"+currEntry->getSpamCount()+":"+currEntry->getHamCount()+":"+currEntry->getSpamPercentage()+":"+currEntry->getHamPercentage()); 
			} 
			return sB->ToString(); 
		}; 
	}; 

	public ref class Repository 
	{ 
	private: 
		FileStream^ outputFile; 
		FileStream^ currentEmail; 
		bool isSpam; 
		PreRepository^ preR;         

	public:  
		Repository(String^ oFile,FileMode fM) 
		{ 
			setOutputFile(oFile, fM);
			preR = gcnew PreRepository(); 
		}
		bool setCurrentEmail(String^ email,bool spam) 
		{ 
			if(File::Exists(email)) 
			{ 
				currentEmail = File::Open(email,FileMode::Open); 
				return true; 
			} 
			Console::Write("Error Opening File {0}...",email); 
			return false; 
		}; 
		//Should be called after setCurrentEmail. Actually creates the Entry objects that PreRepository holds. PreRepository handles all of 
		//the duplication checking. WARNING: WILL NOT CHECK FOR DUPLICATES IN THE OUTPUT FILE THAT MIGHT HAVE ALREADY BEEN THERE. 
		void createEntries() 
		{ 
			StreamReader^ sR = gcnew StreamReader(currentEmail);     
			String^ line; 
			String^ delim = " \n"; 
			while((line = sR->ReadLine())!=nullptr) 
			{ 
				array<String^>^ words = line->Split(delim->ToCharArray()); 
				for(int i=0; i<words->Length; i++) 
				{ 
					preR->addEntry(words[i],isSpam); 
				} 
			} 
		} 
		bool setOutputFile(String^ oFile, FileMode fM) 
		{ 
			if(File::Exists(oFile)) 
			{ 
				outputFile = File::Open(oFile,fM); 
				return true; 
			} 
			outputFile=File::Create(oFile); 
			Console::Write("File {0} does not exist. Has now been created...",oFile); 
			return false; 
		} 
		//Uses a convenience method in PreRepository to get the Entries as a new line and colon delimited String. This will write to the 
		//output file, so the name is a misnomer for now. WARNING: WILL NOT CHECK FOR DUPLICATES IN THE OUTPUT FILE THAT MIGHT HAVE ALREADY BEEN THERE. 
		void buildOutputFile() 
		{ 
			String^ entries = preR->getEntriesAsString(); 
			StreamWriter^ out = gcnew StreamWriter(outputFile); 
			out->Write(entries); 
			out->Flush(); 
			out->Close(); 
		};
	}; 
}